package projectbs.session;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import projectbs.cmd.CommandFactory;
import common.socket.packet.Packet;
import common.socket.packet.PacketBuilder;
import common.socket.serialize.FixedSizeSerializable;
import common.socket.serialize.Serializable;
import common.socket.session.AioSession;
import common.socket.utils.PrintStackTrace;

public final class AioTcpSession extends AioSession {

	private static final Logger logger = LoggerFactory
			.getLogger(AioTcpSession.class);
	private PacketBuilder packetBuilder = new PacketBuilder() {
		@Override
		public void queue(ByteBuffer buffer, AioSession session) {
			List<Packet> packets = this.parse(buffer, session);
			for (Packet packet : packets) {
				CommandFactory.getInstance().publish(packet);
			}
		}
	};

	public AioTcpSession(AsynchronousSocketChannel channel) {
		super(channel);
	}

	@Override
	public synchronized void close() {
		try {
			super.close();
		} catch (IOException e) {
			logger.error("AioTcpSession.close, " + e.getMessage());
			PrintStackTrace.print(logger, e);
		}
	}

	@Override
	protected void queuePacket(ByteBuffer buffer) {
		this.packetBuilder.queue(buffer, this);
	}

	public void response(int cmd, byte[] b) {

		if (cmd > 65535)
			throw new IllegalArgumentException(
					"command cannot be larger than 65535.");

		int length = 4 + b.length;
		
		if(length > Short.MAX_VALUE){
			throw new IllegalArgumentException(
					"command cannot be larger than 65535.");
		}

		ByteBuffer bb = ByteBuffer.allocate(length);
		bb.order(ByteOrder.LITTLE_ENDIAN);
		bb.putShort((short) length);
		bb.putShort((short) cmd);
		bb.put(b);

		bb.flip();

		this.pushWriteData(bb);
	}

	public void response(int cmd, ByteBuffer bb) {
		bb.flip();
		int limit = bb.limit();
		byte[] source = new byte[limit];
		bb.get(source);
		this.response(cmd, source);
	}

	public void response(int cmd, Serializable data) {
		this.response(cmd, data, Short.MAX_VALUE);
	}

	public void response(int cmd, FixedSizeSerializable data) {
		this.response(cmd, data, data.getSize());
	}

	private void response(int cmd, Serializable data, int dataSize) {
		ByteBuffer bb = ByteBuffer.allocate(dataSize);
		data.serialize(bb);
		int limit = bb.limit();
		byte[] source = new byte[limit];
		bb.get(source);
		this.response(cmd, source);
	}

}
